/*
 * pgp-compress.c
 *	  通过 zlib 进行 ZIP 和 ZLIB 压缩。
 *
 * Copyright (c) 2005 Marko Kreen
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *	  notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *	  notice, this list of conditions and the following disclaimer in the
 *	  documentation和/或其他提供的材料与分发一起。
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, 或利润; 或业务中断)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * contrib/pgcrypto/pgp-compress.c
 */

#include "postgres.h"

#include "pgp.h"
#include "px.h"

/*
 * 压缩数据包写入器
 */

#ifdef HAVE_LIBZ

#include <zlib.h>

#define ZIP_OUT_BUF 8192
#define ZIP_IN_BLOCK 8192

struct ZipStat
{
	uint8		type;
	int			buf_len;
	int			hdr_done;
	z_stream	stream;
	uint8		buf[ZIP_OUT_BUF];
};

static void * fc_z_alloc(void *fc_priv, unsigned fc_n_items, unsigned fc_item_len)
{
	return palloc(fc_n_items * fc_item_len);
}

static void fc_z_free(void *fc_priv, void *fc_addr)
{
	pfree(fc_addr);
}

static int fc_compress_init(PushFilter *fc_next, void *fc_init_arg, void **fc_priv_p)
{
	int			fc_res;
	struct ZipStat *fc_st;
	PGP_Context *fc_ctx = fc_init_arg;
	uint8		fc_type = fc_ctx->compress_algo;

	if (fc_type != PGP_COMPR_ZLIB && fc_type != PGP_COMPR_ZIP)
		return PXE_PGP_UNSUPPORTED_COMPR;

	/*
	 * 初始化
	 */
	fc_st = palloc0(sizeof(*fc_st));
	fc_st->buf_len = ZIP_OUT_BUF;
	fc_st->stream.zalloc = fc_z_alloc;
	fc_st->stream.zfree = fc_z_free;

	if (fc_type == PGP_COMPR_ZIP)
		fc_res = deflateInit2(&fc_st->stream, fc_ctx->compress_level,
						   Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY);
	else
		fc_res = deflateInit(&fc_st->stream, fc_ctx->compress_level);
	if (fc_res != Z_OK)
	{
		pfree(fc_st);
		return PXE_PGP_COMPRESSION_ERROR;
	}
	*fc_priv_p = fc_st;

	return ZIP_IN_BLOCK;
}

/* 写入压缩数据包 */

/* 可以处理零长度的输入数据，但不应该 */
static int fc_compress_process(PushFilter *fc_next, void *fc_priv, const uint8 *fc_data, int fc_len)
{
	int			fc_res,
				fc_n_out;
	struct ZipStat *fc_st = fc_priv;

	/*
	 * 处理数据
	 */
	fc_st->stream.next_in = unconstify(uint8 *, fc_data);
	fc_st->stream.avail_in = fc_len;
	while (fc_st->stream.avail_in > 0)
	{
		fc_st->stream.next_out = fc_st->buf;
		fc_st->stream.avail_out = fc_st->buf_len;
		fc_res = deflate(&fc_st->stream, Z_NO_FLUSH);
		if (fc_res != Z_OK)
			return PXE_PGP_COMPRESSION_ERROR;

		fc_n_out = fc_st->buf_len - fc_st->stream.avail_out;
		if (fc_n_out > 0)
		{
			fc_res = pushf_write(fc_next, fc_st->buf, fc_n_out);
			if (fc_res < 0)
				return fc_res;
		}
	}

	return 0;
}

static int fc_compress_flush(PushFilter *fc_next, void *fc_priv)
{
	int			fc_res,
				fc_zres,
				fc_n_out;
	struct ZipStat *fc_st = fc_priv;

	fc_st->stream.next_in = NULL;
	fc_st->stream.avail_in = 0;
	while (1)
	{
		fc_st->stream.next_out = fc_st->buf;
		fc_st->stream.avail_out = fc_st->buf_len;
		fc_zres = deflate(&fc_st->stream, Z_FINISH);
		if (fc_zres != Z_STREAM_END && fc_zres != Z_OK)
			return PXE_PGP_COMPRESSION_ERROR;

		fc_n_out = fc_st->buf_len - fc_st->stream.avail_out;
		if (fc_n_out > 0)
		{
			fc_res = pushf_write(fc_next, fc_st->buf, fc_n_out);
			if (fc_res < 0)
				return fc_res;
		}
		if (fc_zres == Z_STREAM_END)
			break;
	}
	return 0;
}

static void fc_compress_free(void *fc_priv)
{
	struct ZipStat *fc_st = fc_priv;

	deflateEnd(&fc_st->stream);
	px_memset(fc_st, 0, sizeof(*fc_st));
	pfree(fc_st);
}

static const PushFilterOps
			compress_filter = {
	fc_compress_init, fc_compress_process, fc_compress_flush, fc_compress_free
};

int pgp_compress_filter(PushFilter **fc_res, PGP_Context *fc_ctx, PushFilter *fc_dst)
{
	return pushf_create(fc_res, &compress_filter, fc_ctx, fc_dst);
}

/*
 * 解压缩
 */
struct DecomprData
{
	int			buf_len;		/* = ZIP_OUT_BUF */
	int			buf_data;		/* 可用数据 */
	uint8	   *pos;
	z_stream	stream;
	int			eof;
	uint8		buf[ZIP_OUT_BUF];
};

static int fc_decompress_init(void **fc_priv_p, void *fc_arg, PullFilter *fc_src)
{
	PGP_Context *fc_ctx = fc_arg;
	struct DecomprData *fc_dec;
	int			fc_res;

	if (fc_ctx->compress_algo != PGP_COMPR_ZLIB
		&& fc_ctx->compress_algo != PGP_COMPR_ZIP)
		return PXE_PGP_UNSUPPORTED_COMPR;

	fc_dec = palloc0(sizeof(*fc_dec));
	fc_dec->buf_len = ZIP_OUT_BUF;
	*fc_priv_p = fc_dec;

	fc_dec->stream.zalloc = fc_z_alloc;
	fc_dec->stream.zfree = fc_z_free;

	if (fc_ctx->compress_algo == PGP_COMPR_ZIP)
		fc_res = inflateInit2(&fc_dec->stream, -15);
	else
		fc_res = inflateInit(&fc_dec->stream);
	if (fc_res != Z_OK)
	{
		pfree(fc_dec);
		px_debug("decompress_init: inflateInit error");
		return PXE_PGP_COMPRESSION_ERROR;
	}

	return 0;
}

static int fc_decompress_read(void *fc_priv, PullFilter *fc_src, int fc_len,
				uint8 **fc_data_p, uint8 *fc_buf, int fc_buflen)
{
	int			fc_res;
	int			fc_flush;
	struct DecomprData *fc_dec = fc_priv;

restart:
	if (fc_dec->buf_data > 0)
	{
		if (fc_len > fc_dec->buf_data)
			fc_len = fc_dec->buf_data;
		*fc_data_p = fc_dec->pos;
		fc_dec->pos += fc_len;
		fc_dec->buf_data -= fc_len;
		return fc_len;
	}

	if (fc_dec->eof)
		return 0;

	if (fc_dec->stream.avail_in == 0)
	{
		uint8	   *fc_tmp;

		fc_res = pullf_read(fc_src, 8192, &fc_tmp);
		if (fc_res < 0)
			return fc_res;
		fc_dec->stream.next_in = fc_tmp;
		fc_dec->stream.avail_in = fc_res;
	}

	fc_dec->stream.next_out = fc_dec->buf;
	fc_dec->stream.avail_out = fc_dec->buf_len;
	fc_dec->pos = fc_dec->buf;

	/*
	 * Z_SYNC_FLUSH 是告诉 zlib 尽可能多地输出。无论如何它都应该这样做
	 * (Z_NO_FLUSH)，但似乎保留不这样做的权利。所以让我们
	 * 遵循 API。
	 */
	fc_flush = fc_dec->stream.avail_in ? Z_SYNC_FLUSH : Z_FINISH;
	fc_res = inflate(&fc_dec->stream, fc_flush);
	if (fc_res != Z_OK && fc_res != Z_STREAM_END)
	{
		px_debug("decompress_read: inflate error: %d", fc_res);
		return PXE_PGP_CORRUPT_DATA;
	}

	fc_dec->buf_data = fc_dec->buf_len - fc_dec->stream.avail_out;
	if (fc_res == Z_STREAM_END)
	{
		uint8	   *fc_tmp;

		/*
		 * 一个流必须由一个正常的数据包结束。如果源流中的最后一个流
		 * 数据包是一个完整的数据包，则必须跟随一个正常的空数据包。
		 * 由于底层的数据包读取器并不知道压缩流已经结束，我们需要在
		 * 此处消耗结束数据包。即使流已经结束，这次读取也不会造成损害。
		 */
		fc_res = pullf_read(fc_src, 1, &fc_tmp);

		if (fc_res < 0)
			return fc_res;
		else if (fc_res > 0)
		{
			px_debug("decompress_read: extra bytes after end of stream");
			return PXE_PGP_CORRUPT_DATA;
		}
		fc_dec->eof = 1;
	}
	goto restart;
}

static void fc_decompress_free(void *fc_priv)
{
	struct DecomprData *fc_dec = fc_priv;

	inflateEnd(&fc_dec->stream);
	px_memset(fc_dec, 0, sizeof(*fc_dec));
	pfree(fc_dec);
}

static const PullFilterOps
			decompress_filter = {
	fc_decompress_init, fc_decompress_read, fc_decompress_free
};

int pgp_decompress_filter(PullFilter **fc_res, PGP_Context *fc_ctx, PullFilter *fc_src)
{
	return pullf_create(fc_res, &decompress_filter, fc_ctx, fc_src);
}
#else							/* !HAVE_LIBZ */

int pgp_compress_filter(PushFilter **res, PGP_Context *ctx, PushFilter *dst)
{
	return PXE_PGP_UNSUPPORTED_COMPR;
}

int pgp_decompress_filter(PullFilter **res, PGP_Context *ctx, PullFilter *src)
{
	return PXE_PGP_UNSUPPORTED_COMPR;
}

#endif
